﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Processing;
using Cloo;
using Processing.Structures;

namespace RayTracer.Kernels
{
    /// <summary>
    /// Lichtstrahlensichtbarkeit mit einem Objekt: Hierbei gilt es die Sichtbarkeit
    /// und auch Schnittpunkt und Richtung der Lichtstrahlen in Bezug zu einem 
    /// Schnittpunkt mit einem Objekt zu er-mitteln.
    /// </summary>
    abstract class AKernelSimpleLightPrimitive : AKernel
    {
        #region Attribute
        #region GPU
        protected Buffer<Float4> gpu_surInter;
        protected Buffer<Float4> gpu_surNormal;
        protected Buffer<int> gpu_oldVis;
        protected Buffer<Float4> gpu_ligPos;
        protected Buffer<Float4> gpu_objPos;
        protected Buffer<Float4> gpu_objRot;

        protected Buffer<int> gpu_ligVis;
        protected Buffer<Float4> gpu_ligDir;
        #endregion GPU
        #endregion Attribute

        #region Eigenschaften
        #region CPU Dateninitialisierung
        /// <summary>
        /// Gibt und setzt die Zeilschnittpunkte.
        /// </summary>
        public Buffer<Float4> SurfaceIntersections { get { return gpu_surInter; } set { gpu_surInter = value; } }

        /// <summary>
        /// Gibt und setzt die Richtungen der Zielflächen.
        /// </summary>
        public Buffer<Float4> SurfaceNormals { get { return gpu_surNormal; } set { gpu_surNormal = value; } }

        /// <summary>
        /// Gibt und setzt die alten Sichtbarkeiten.
        /// </summary>
        public Buffer<int> OldVisibilities { get { return gpu_oldVis; } set { gpu_oldVis = value; } }

        /// <summary>
        /// Gibt und setzt die Positionen der Lichter.
        /// </summary>
        public Buffer<Float4> LightPositions { get { return gpu_ligPos; } set { gpu_ligPos = value; } }

        /// <summary>
        /// Gibt und setzt die Positionen der Objekte.
        /// </summary>
        public Buffer<Float4> ObjectPositions { get { return gpu_objPos; } set { gpu_objPos = value; } }

        /// <summary>
        /// Gibt und setzt die Rotationen der Objekte.
        /// </summary>
        public Buffer<Float4> ObjectRotations { get { return gpu_objRot; } set { gpu_objRot = value; } }

        /// <summary>
        /// Gibt die Indikatoren ob die Lichter sichtbar sind.
        /// </summary>
        public Buffer<int> LightsVisibilities
        {
            get
            {
                if (State != KernelState.Done) throw new Exception("Die Kernel wurde noch nicht berechnet.");
                return gpu_ligVis;
            }
            set { gpu_ligVis = value; }
        }

        /// <summary>
        /// Gibt die Richtungen der Lichter an.
        /// </summary>
        public Buffer<Float4> LightsDirections
        {
            get
            {
                if (State != KernelState.Done) throw new Exception("Die Kernel wurde noch nicht berechnet.");
                return gpu_ligDir;
            }
            set { gpu_ligDir = value; }
        }
        #endregion CPU Dateninitialisierung

        #region Kernelausführung
        /// <summary>
        /// Gibt die Dimension des Programms an.
        /// </summary>
        protected override long[] Dimensions { get { return new long[] { gpu_surInter.Length }; } }

        /// <summary>
        /// Gibt an ob die Kerneldaten valid initialisiert sind.
        /// </summary>
        protected override bool IsValidInitialized
        {
            get
            {
                if (gpu_surInter == null) throw new RayTracerException("Die Oberflächenschnittpunkte müssen angegeben werden.");
                if (gpu_surNormal == null) throw new RayTracerException("Die Oberflächenausrichtungen müssen angegeben werden.");
                if (gpu_ligPos == null) throw new RayTracerException("Die Lichtpositionen müssen angegeben werden.");
                if (gpu_objPos == null) throw new RayTracerException("Die Objektpositionen müssen angegeben werden.");
                if (gpu_objRot == null) throw new RayTracerException("Die Objektrotationen müssen angegeben werden.");
                return gpu_surInter.Length == gpu_surNormal.Length &&
                    gpu_objPos.Length == gpu_objRot.Length &&
                    gpu_surInter.Length > 0;
            }
        }
        #endregion Kernelausführung
        #endregion Eigenschaften

        #region Methoden
        /// <summary>      
        /// Instanziiert und initialisiert die Kernel.
        /// </summary>
        /// <param name="processor">Der Prozessor.</param>
        public AKernelSimpleLightPrimitive(Processor processor) : base(processor) { }

        /// <summary>
        /// Führt Aktionen vor der Ausführung der Kernel - mit dem Run-Aufruf aus. (z. B. Datentransport von CPU zu GPU)
        /// </summary>
        protected override void PrepareKernelExecution()
        {
            base.PrepareKernelExecution();
            if (gpu_oldVis == null) gpu_oldVis = Processor.AllocateBuffer<int>(gpu_surInter.Length * gpu_ligPos.Length);

            gpu_ligVis = Processor.AllocateBuffer<int>(gpu_surInter.Length * gpu_ligPos.Length);
            if (gpu_ligDir == null) gpu_ligDir = Processor.AllocateBuffer<Float4>(gpu_surInter.Length * gpu_ligPos.Length);
        }
        #endregion Methoden
    }
}
